// eee.Sheffield.PZ.Math
//
// Copyright ?Ping Zou, 2007
// sg71.cherub@gmail.com

namespace eee.Sheffield.PZ.Imaging
{
	using System;
	using System.Drawing;
	using System.Drawing.Imaging;
    using eee.Sheffield.PZ.Math;

	/// <summary>
	/// interpolation methods
	/// </summary>
	public class Interpolate
	{
		private PZPoint p;		// point
		/// <summary>
		/// 0 - NEAREST_NEIGHBOUR
		/// 1 - LINEAR
		/// 2 - CUBIC
		/// 3 - QUINTIC
		/// 4 - SEPTIC
		/// 5 - BLACKMANHARRIS
		/// </summary>
		private int im = 0;				// interpolate method
		private int nKernelHalfWidth = 0;	// kernal half width
		private double[] pIntermediateI = null;	// array of vex's value
		private PZInterpolateFunction pfInterpolationFn = null;	// interpolate function delegate
		
		public double I = 0.0;		// return interpolate value

		/// <summary>
		/// empty constructor
		/// </summary>
		public Interpolate() {}

		/// <summary>
		/// constructor
		/// </summary>
		/// <param name="inputp">point to interpolate</param>
		/// <param name="inputim">interpolate method</param>
		public Interpolate(PZPoint inputp, int inputim)
		{
			p = inputp;
			im = inputim;
			switch (im)
			{
				case 0:
					break;

				case 1:
					nKernelHalfWidth = 1;
					pIntermediateI = new double[3];
					pfInterpolationFn = new PZInterpolateFunction(PZInterpolate.LinearInterpolationFn);
					break;

				case 2:
					nKernelHalfWidth = 2;
					pIntermediateI = new double[5];
					pfInterpolationFn = new PZInterpolateFunction(PZInterpolate.CubicInterpolationFn);
					break;

				case 3:
					nKernelHalfWidth = 3;
					pIntermediateI = new double[7];
					pfInterpolationFn = new PZInterpolateFunction(PZInterpolate.QuinticInterpolationFn);
					break;

				case 4:
					nKernelHalfWidth = 4;
					pIntermediateI = new double[9];
					pfInterpolationFn = new PZInterpolateFunction(PZInterpolate.SepticInterpolationFn);
					break;

				case 5:
					nKernelHalfWidth = 3;
					pIntermediateI = new double[7];
					pfInterpolationFn = new PZInterpolateFunction(PZInterpolate.BlackmanHarrisInterpolationFn);
					break;

				default:
					PZMath_errno.ERROR ("PZInterpolateScanline(PZPoint, int): Unrecognised interpolation method");
					break;
			} // switch 

		} // PZInterpolateScanline(PZPoint, int)


		/// <summary>
		/// property p
		/// </summary>
		public PZPoint P
		{
			set {p = value;}
			get {return P;}
		}
		
		/// <summary>
		/// set interpolate method
		/// </summary>
		public void SetIterpolateMethod(int i)
		{
			im = i;
			switch (im)
			{
				case 0:
					break;

				case 1:
					nKernelHalfWidth = 1;
					pIntermediateI = new double[3];
					pfInterpolationFn = new PZInterpolateFunction(PZInterpolate.LinearInterpolationFn);
					break;

				case 2:
					nKernelHalfWidth = 2;
					pIntermediateI = new double[5];
					pfInterpolationFn = new PZInterpolateFunction(PZInterpolate.CubicInterpolationFn);
					break;

				case 3:
					nKernelHalfWidth = 3;
					pIntermediateI = new double[7];
					pfInterpolationFn = new PZInterpolateFunction(PZInterpolate.QuinticInterpolationFn);
					break;

				case 4:
					nKernelHalfWidth = 4;
					pIntermediateI = new double[9];
					pfInterpolationFn = new PZInterpolateFunction(PZInterpolate.SepticInterpolationFn);
					break;

				case 5:
					nKernelHalfWidth = 3;
					pIntermediateI = new double[7];
					pfInterpolationFn = new PZInterpolateFunction(PZInterpolate.BlackmanHarrisInterpolationFn);
					break;

				default:
					PZMath_errno.ERROR ("PZInterpolateScanline(PZPoint, int): Unrecognised interpolation method");
					break;
			} // switch 
		} // InterpolateMethod()

		/// <summary>
		/// interpolate, I is the return value
		/// </summary>
		/// <param name="srcImg"></param>
		/// <returns></returns>
		public Bitmap Apply(Bitmap srcImg)
		{
			
			//-- prepare srcImg
			int nMinCol = 0;
			int nMaxCol = srcImg.Width;
			int nMinRow = 0;
			int nMaxRow = srcImg.Height;
            //// convert input to grayscale if it's not yet
            //bool disposeSource = false;
            //if (srcImg.PixelFormat != PixelFormat.Format8bppIndexed)
            //{
            //    disposeSource = true;
            //    // create grayscale image
            //    IFilter filter = new GrayscaleRMY();
            //    srcImg = filter.Apply(srcImg);
            //}
			
			//-- interpolate
			int i,j;
			for (i = 0; i < pIntermediateI.Length; i ++)
				pIntermediateI[i] = 0.0;
			double x_in = p.x;
			double y_in = p.y;
			// Perform resampling of input image
			int nRoundX = (int) System.Math.Round(x_in);
			int nRoundY = (int) System.Math.Round(y_in);
			I = 0;	// Interpolated intensity

			// Interpolate image intensity
	
			if(im == 0)	
				// NEAREST_NEIGHBOUR
				if(nRoundX >= nMinCol && nRoundX < nMaxCol && nRoundY >= nMinRow && nRoundY < nMaxRow)
					I = (double) srcImg.GetPixel (nRoundY,nRoundX).R;
				else 
					I = 0;
			else
			{
				// Interpolate intermediate intensity values
				int k;
				//memset();	// Set intermediate intensities to zero, automatically

				double x;
				for(j = nRoundY - nKernelHalfWidth; j <= nRoundY + nKernelHalfWidth; j++)
					for(i = nRoundX - nKernelHalfWidth; i <= nRoundX + nKernelHalfWidth; i++)
						if(i >= nMinCol && i < nMaxCol && j >= nMinRow && j < nMaxRow)
						{
							x = (double) i - x_in;
							//assert(j - nRoundY + nKernelHalfWidth >= 0 && j - nRoundY + nKernelHalfWidth < 2 * nKernelHalfWidth + 1);
							pIntermediateI[j - nRoundY + nKernelHalfWidth] += (double) srcImg.GetPixel (i,j).R  * pfInterpolationFn(x);
						}

				// Interpolate final intensity value
				double y;
				I = 0;
				for(k = 0; k < 2*nKernelHalfWidth + 1; k++)
				{
					y = (double) (nRoundY - nKernelHalfWidth + k) - y_in;
					I += pIntermediateI[k] * pfInterpolationFn(y);
				}
			} // end of else

            //if (disposeSource)
            //    srcImg.Dispose();
			// -- return an useless dstImg
			// create new image
			Bitmap dstImg = new Bitmap(nMaxCol, nMaxRow, PixelFormat.Format8bppIndexed);
			return dstImg;
			
		} // Apply()
	}
}
